Skip to content

fix(MonkeyPatch): register undo entry only after a successful mutation (delattr/setitem/delitem) (#14909) - #14910

Open
sharonyao1127 wants to merge 3 commits into
pytest-dev:mainfrom
sharonyao1127:fix-monkey-patch-delattr-setitem-delitem-stale-undo
Open

fix(MonkeyPatch): register undo entry only after a successful mutation (delattr/setitem/delitem) (#14909)#14910
sharonyao1127 wants to merge 3 commits into
pytest-dev:mainfrom
sharonyao1127:fix-monkey-patch-delattr-setitem-delitem-stale-undo

Conversation

@sharonyao1127

Copy link
Copy Markdown

What

Fixes #14909.

MonkeyPatch.delattr, MonkeyPatch.setitem and MonkeyPatch.delitem registered their undo entry before attempting the underlying mutation. When the mutation raised (e.g. delattr on a slotted attribute, or setitem/delitem on a read-only MappingProxyType), the target was left unchanged but a stale undo entry remained on the stack. Later, undo() (called automatically at fixture teardown) tried to revert a change that never happened, producing a spurious teardown error.

How

Applied the same ordering already used by MonkeyPatch.setattr after #14209:

  • save the original value,
  • attempt the mutation,
  • register the undo entry only after the mutation succeeds.

The success-case behavior of each method is unchanged.

Tests

Added regression tests mirroring the reproduction in the issue:

  • test_failed_delattrdelattr on a slotted attribute,
  • test_failed_setitemsetitem on a MappingProxyType,
  • test_failed_delitemdelitem on a MappingProxyType.

Each asserts the target is unchanged after the expected exception and that undo() does not raise (i.e. no stale undo entry is left behind). A changelog fragment was added.

@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided (automation) changelog entry is part of PR label Aug 19, 2026
@Amine-LG

Copy link
Copy Markdown

Just for context, I had already been working on this before opening #14909 and had a tested fix with regression tests ready locally. I was waiting for assignment/maintainer feedback before opening my PR, as I mentioned in the issue.

Happy to follow the maintainers preference on how to proceed with the overlapping work.

@sharonyao1127
sharonyao1127 force-pushed the fix-monkey-patch-delattr-setitem-delitem-stale-undo branch 2 times, most recently from d10c3b6 to 020f8f1 Compare August 20, 2026 07:52
…n (delattr/setitem/delitem) (pytest-dev#14909)

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
@sharonyao1127
sharonyao1127 force-pushed the fix-monkey-patch-delattr-setitem-delitem-stale-undo branch from 020f8f1 to a88e91b Compare August 20, 2026 07:58

@NikolayAir NikolayAir left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I verified locally that the three regression tests for failed delattr, setitem, and delitem operations fail against the PR base implementation because a failed operation still leaves an undo entry behind even though no change was made. That entry can later cause undo() or automatic test cleanup to fail. All three tests pass on the PR head.

testing/test_monkeypatch.py also passes locally (39 passed, 1 skipped). The implementation now records each undo entry only after the corresponding operation succeeds, matching the existing setattr pattern. Based on these checks, the fix looks correct for the reported cases.

Comment thread testing/test_monkeypatch.py Outdated
monkeypatch.undo()


@pytest.mark.parametrize("make_mapping", [dict])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional: make_mapping is parametrized here, but the test doesn't use it. Instead, inner is created directly as a dict. Since dict is the only value listed here, would it be clearer to remove the parametrization?

If additional mapping types are intended, using make_mapping(...) to create inner would make that intent explicit.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks. I pushed 2802b66 to remove the unused parametrization and keep the test as the single concrete dict case it actually exercises.

Validation: . .venv/bin/activate && python -m pytest testing/test_monkeypatch.py -q -> 39 passed, 1 skipped.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick update. Removing the unused parametrization makes the test clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided (automation) changelog entry is part of PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MonkeyPatch records stale undo entries when delattr/setitem/delitem fails

3 participants